package com.test;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.api.data.FeatureSource;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.feature.FeatureCollection;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.index.SpatialIndex;
import org.locationtech.jts.index.quadtree.Quadtree;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GadmQuery {
    
    // 单例模式的实例
    private static volatile GadmQuery instance;
    
    // GeoPackage数据存储
    private DataStore dataStore;
    
    // 图层列表和排序后的图层
    private String[] layerNames;
    private List<String> sortedLayers;
    
    // 空间索引缓存，用于快速空间查询
    private Map<String, SpatialIndex> spatialIndices = new ConcurrentHashMap<>();
    
    // 属性信息缓存
    private Map<String, Map<Object, Map<String, String>>> layerAttributesCache = new ConcurrentHashMap<>();
    
    // 坐标查询结果缓存（LRU缓存的简化实现）
    private final int CACHE_SIZE = 1000000;
    private ConcurrentHashMap<CoordinateKey, Map<Integer, Map<String, String>>> queryCache = new ConcurrentHashMap<>();
    
    // 线程池用于并发处理
    private ExecutorService executorService;
    
    // 坐标键类，用于缓存的键
    private static class CoordinateKey {
        private final double lon;
        private final double lat;
        private final int precision = 6; // 保留6位小数，平衡精度和缓存命中率
        
        public CoordinateKey(double lon, double lat) {
            // 保留指定小数位数，减少浮点数精度问题
            this.lon = Math.round(lon * Math.pow(10, precision)) / Math.pow(10, precision);
            this.lat = Math.round(lat * Math.pow(10, precision)) / Math.pow(10, precision);
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CoordinateKey that = (CoordinateKey) o;
            return Double.compare(that.lon, lon) == 0 && Double.compare(that.lat, lat) == 0;
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(lon, lat);
        }
    }
    
    /**
     * 私有构造函数，初始化数据存储和索引
     */
    private GadmQuery() throws IOException, URISyntaxException {
        initialize();
    }
    
    /**
     * 获取单例实例
     */
    public static synchronized GadmQuery getInstance() throws IOException, URISyntaxException {
        if (instance == null) {
            synchronized (GadmQuery.class) {
                if (instance == null) {
                    instance = new GadmQuery();
                }
            }
        }
        return instance;
    }
    
    /**
     * 初始化数据存储、图层和空间索引
     */
    private void initialize() throws IOException, URISyntaxException {
        // 加载 GeoPackage
        URL resourceUrl = GadmQuery.class.getClassLoader().getResource("gadm41_MYS.gpkg");
        if (resourceUrl == null) {
            throw new IOException("找不到资源文件: gadm41_MYS.gpkg");
        }
        
        File file = new File(resourceUrl.toURI());
        if (!file.exists()) {
            throw new IOException("GeoPackage 文件不存在: " + file.getAbsolutePath());
        }

        Map<String, Object> params = new HashMap<>();
        params.put("database", file.getAbsolutePath());
        params.put("dbtype", "geopkg");
        
        // 尝试获取数据存储
        dataStore = DataStoreFinder.getDataStore(params);
        if (dataStore == null) {
            Map<String, Object> altParams = new HashMap<>();
            altParams.put("dbtype", "sqlite");
            altParams.put("database", file.getAbsolutePath());
            dataStore = DataStoreFinder.getDataStore(altParams);
        }
        
        if (dataStore == null) {
            throw new IOException("无法打开 GeoPackage/SQLite 文件，请检查依赖是否正确");
        }
        
        // 获取图层并排序
        layerNames = dataStore.getTypeNames();
        sortedLayers = sortLayersByAdminLevel(layerNames);
        
        // 初始化线程池
        int processorCount = Runtime.getRuntime().availableProcessors();
        executorService = Executors.newFixedThreadPool(processorCount * 2);
        
        // 预构建空间索引（可以在后台线程中进行）
        buildSpatialIndices();
        
        System.out.println("GadmQuery 初始化完成，找到图层: " + String.join(", ", layerNames));
    }
    
    /**
     * 构建所有图层的空间索引
     */
    private void buildSpatialIndices() {
        for (String layerName : sortedLayers) {
            try {
                SpatialIndex index = new Quadtree();
                Map<Object, Map<String, String>> attrCache = new HashMap<>();
                
                FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(layerName);
                FeatureCollection<SimpleFeatureType, SimpleFeature> collection = source.getFeatures();
                SimpleFeatureType schema = source.getSchema();
                
                try (var features = collection.features()) {
                    while (features.hasNext()) {
                        SimpleFeature feature = features.next();
                        Geometry geom = (Geometry) feature.getDefaultGeometry();
                        
                        if (geom != null) {
                            // 将几何对象添加到空间索引
                            Envelope env = geom.getEnvelopeInternal();
                            index.insert(env, feature);
                            
                            // 缓存关键字段属性
                            Object id = feature.getID();
                            Map<String, String> attrs = new HashMap<>();
                            
                            for (int i = 0; i < schema.getAttributeCount(); i++) {
                                String attrName = schema.getDescriptor(i).getLocalName();
                                if (attrName.contains("NAME") || attrName.contains("GID") || 
                                    attrName.equals("TYPE") || attrName.contains("ADM")) {
                                    Object value = feature.getAttribute(i);
                                    String valueStr = value != null ? value.toString() : "未知";
                                    attrs.put(attrName, valueStr);
                                }
                            }
                            attrCache.put(id, attrs);
                        }
                    }
                }
                
                // 存储索引和属性缓存
                spatialIndices.put(layerName, index);
                layerAttributesCache.put(layerName, attrCache);
                
                System.out.println("图层 " + layerName + " 的空间索引构建完成");
                
            } catch (Exception e) {
                System.err.println("构建图层 " + layerName + " 的空间索引时出错: " + e.getMessage());
            }
        }
    }
    
    /**
     * 按行政级别对图层进行排序
     */
    private List<String> sortLayersByAdminLevel(String[] layerNames) {
        List<String> sortedLayers = new ArrayList<>(Arrays.asList(layerNames));
        sortedLayers.sort(Comparator.comparingInt(this::getAdminLevel));
        return sortedLayers;
    }
    
    /**
     * 从图层名称中提取行政级别
     */
    private int getAdminLevel(String layerName) {
        Pattern pattern = Pattern.compile("(\\d+)$");
        Matcher matcher = pattern.matcher(layerName);
        
        if (matcher.find()) {
            try {
                return Integer.parseInt(matcher.group(1));
            } catch (NumberFormatException e) {
                return Integer.MAX_VALUE;
            }
        }
        
        if (layerName.contains("ADM_0")) return 0;
        if (layerName.contains("ADM_1")) return 1;
        if (layerName.contains("ADM_2")) return 2;
        if (layerName.contains("ADM_3")) return 3;
        if (layerName.contains("ADM_4")) return 4;
        
        return Integer.MAX_VALUE;
    }
    
    /**
     * 查询指定坐标的行政区域信息（核心查询方法）
     */
    public Map<Integer, Map<String, String>> queryAddress(double lon, double lat) {
        // 检查缓存
        CoordinateKey cacheKey = new CoordinateKey(lon, lat);
        Map<Integer, Map<String, String>> cachedResult = queryCache.get(cacheKey);
        if (cachedResult != null) {
            return cachedResult;
        }
        
        // 创建查询点
        Point queryPoint = JTSFactoryFinder.getGeometryFactory().createPoint(new Coordinate(lon, lat));
        
        // 存储所有匹配的行政层级信息
        Map<Integer, Map<String, String>> adminInfo = new HashMap<>();
        
        // 遍历排序后的图层进行查询
        for (String layerName : sortedLayers) {
            try {
                SpatialIndex index = spatialIndices.get(layerName);
                Map<Object, Map<String, String>> attrCache = layerAttributesCache.get(layerName);
                
                if (index != null && attrCache != null) {
                    // 使用空间索引快速过滤
                    List<?> candidates = index.query(queryPoint.getEnvelopeInternal());
                    
                    for (Object candidate : candidates) {
                        SimpleFeature feature = (SimpleFeature) candidate;
                        Geometry geom = (Geometry) feature.getDefaultGeometry();
                        
                        if (geom != null && geom.contains(queryPoint)) {
                            // 找到匹配的行政区，获取其属性
                            int adminLevel = getAdminLevel(layerName);
                            Map<String, String> layerInfo = new HashMap<>(attrCache.get(feature.getID()));
                            adminInfo.put(adminLevel, layerInfo);
                            break; // 每个图层只需要第一个匹配项
                        }
                    }
                }
            } catch (Exception e) {
                // 生产环境应该使用日志框架，而不是System.err
                System.err.println("查询图层 " + layerName + " 时出错: " + e.getMessage());
            }
        }
        
        // 将结果存入缓存（使用简单的LRU策略）
        if (queryCache.size() >= CACHE_SIZE) {
            // 简单LRU：移除最旧的10%的缓存项
            int removeCount = (int) (CACHE_SIZE * 0.1);
            Iterator<Map.Entry<CoordinateKey, Map<Integer, Map<String, String>>>> iterator = queryCache.entrySet().iterator();
            for (int i = 0; i < removeCount && iterator.hasNext(); i++) {
                iterator.next();
                iterator.remove();
            }
        }
        queryCache.put(cacheKey, adminInfo);
        
        return adminInfo;
    }
    
    /**
     * 生成完整的地址描述
     */
    public String generateFullAddress(double lon, double lat) {
        Map<Integer, Map<String, String>> adminInfo = queryAddress(lon, lat);
        
        if (adminInfo.isEmpty()) {
            return "未找到任何匹配的行政区域";
        }
        
        // 获取行政级别并按升序排列
        List<Integer> levels = new ArrayList<>(adminInfo.keySet());
        Collections.sort(levels);
        
        // 构建完整地址
        StringBuilder fullAddress = new StringBuilder();
        
        for (Integer level : levels) {
            Map<String, String> info = adminInfo.get(level);
            String name = findNameField(info);
            
            if (!name.isEmpty()) {
                if (fullAddress.length() > 0) {
                    fullAddress.append(", ");
                }
                fullAddress.append(name);
            }
        }
        
        // 如果没有找到名称字段，尝试使用GID_0作为基本信息
        if (fullAddress.length() == 0) {
            if (adminInfo.containsKey(0) && adminInfo.get(0).containsKey("GID_0")) {
                String countryCode = adminInfo.get(0).get("GID_0");
                String country = countryCode.equals("MYS") ? "马来西亚" : countryCode;
                return country;
            }
            return "无法生成友好地址描述";
        }
        
        return fullAddress.toString();
    }
    
    /**
     * 尝试从属性中找到最适合的名称字段
     */
    private String findNameField(Map<String, String> attributes) {
        List<String> possibleNameFields = Arrays.asList(
            "NAME", "NAME_1", "NAME_2", "NAME_3", "NAME_4", 
            "name", "name_1", "name_2", "name_3", "name_4",
            "NAME_EN", "NAME_ENG"
        );
        
        for (String field : possibleNameFields) {
            if (attributes.containsKey(field) && !"未知".equals(attributes.get(field))) {
                return attributes.get(field);
            }
        }
        
        // 如果没有找到标准的名称字段，尝试查找其他可能包含名称的字段
        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            
            if ((key.contains("NAME") || key.contains("name")) && !"未知".equals(value)) {
                return value;
            }
        }
        
        return "";
    }
    
    /**
     * 关闭资源
     */
    public void shutdown() {
        try {
            if (dataStore != null) {
                dataStore.dispose();
            }
            if (executorService != null && !executorService.isShutdown()) {
                executorService.shutdown();
            }
            // 清空缓存
            spatialIndices.clear();
            layerAttributesCache.clear();
            queryCache.clear();
            instance = null;
            System.out.println("GadmQuery 资源已释放");
        } catch (Exception e) {
            System.err.println("关闭GadmQuery时出错: " + e.getMessage());
        }
    }
    
    /**
     * 主方法 - 示例用法
     */
    public static void main(String[] args) {
        try {
            // 初始化单例
            GadmQuery queryService = GadmQuery.getInstance();
            
            // 构造查询点（例如：吉隆坡某地）
            double lon = 101.6869;
            double lat = 3.1390;
            
            // 性能测试：运行100次查询并测量时间
            long startTime = System.currentTimeMillis();
            int testRuns = 100;
            
            for (int i = 0; i < testRuns; i++) {
                String address = queryService.generateFullAddress(lon, lat);
                if (i == 0) { // 只输出一次结果
                    System.out.println("地址查询结果: " + address);
                }
            }
            
            long endTime = System.currentTimeMillis();
            double avgTime = (endTime - startTime) / (double) testRuns;
            System.out.println("完成 " + testRuns + " 次查询，平均每次查询耗时: " + avgTime + " ms");
            
            // 对于实际的高并发应用，应该在应用关闭时调用shutdown
            // queryService.shutdown();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}